package io.hellsinger.filesystem.size

import io.hellsinger.filesystem.size.Size.Companion.Bit
import io.hellsinger.filesystem.size.Size.Companion.KiB
import io.hellsinger.filesystem.size.Size.Companion.NoType
import io.hellsinger.filesystem.size.Size.Companion.YiB
import java.text.DecimalFormat
import kotlin.math.pow

abstract class SizeWrapper(
    override val original: Long,
) : Size {
    final override var type: Int = NoType
        private set

    private var formatter: DecimalFormat? = null

    override val name: String
        get() = onCreateTypeTitle(type)

    override fun plus(other: Size): Size = onCreateSize(original + other.original)

    override fun minus(other: Size): Size =
        onCreateSize(
            if (other.original > original) other.original - original else original - other.original,
        )

    override fun equals(other: Any?): Boolean {
        if (other === this) return true
        if (other !is Size) return false

        return original == other.original
    }

    override fun format(pattern: String?): Size {
        formatter = if (pattern != null) DecimalFormat(pattern) else null
        return this
    }

    override fun toString(): String {
        if (type != NoType) return createStringType()
        return toStringOriginal()
    }

    private fun toStringOriginal(): String {
        if (original < delimiter) {
            type = Bit
            return "$original${onCreateTypeTitle(Bit)}"
        }

        var original = this.original / delimiter
        type = KiB

        // TODO: Check if cycle unwinding makes badass things
        while (original >= delimiter) {
            if (type > YiB) break
            type += 1
            original /= delimiter
        }

        return createStringType()
    }

    override fun compareTo(other: Size) = original.compareTo(other.original)

    private fun createStringType(): String {
        if (formatter != null) {
            return formatter!!.format(
                original / delimiter.toFloat().pow(type),
            ) + onCreateTypeTitle(type)
        }
        return "${original / delimiter.toFloat().pow(type)}${onCreateTypeTitle(type)}"
    }

    abstract fun onCreateTypeTitle(type: Int): String

    abstract fun onCreateSize(original: Long): Size

    override fun hashCode(): Int {
        var result = original.hashCode()
        result = 31 * result + type
        return result
    }
}
